﻿Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Reflection
Imports System.Runtime.CompilerServices

Namespace System.Workflow.ComponentModel
    <DefaultMember("Item")> _
    Friend Class ItemList([Of] T)
        Inherits List([Of] T)
        Implements IList([Of] T), ICollection([Of] T), IEnumerable([Of] T), IList, ICollection, IEnumerable
        ' Events
        Public Custom Event ListChanged As ItemListChangeEventHandler([Of] T)
        Friend Custom Event ListChanging As ItemListChangeEventHandler([Of] T)

        ' Methods
        Friend Sub New(ByVal owner As Object)
            Me.owner = owner
        End Sub

        Public Sub Add(ByVal item As T)
            DirectCast(Me, ICollection([Of] T)).Add(item)
        End Sub

        Public Sub AddRange(ByVal collection As IEnumerable([Of] T))
            If (collection Is Nothing) Then
                Throw New ArgumentNullException("collection")
            End If
            Me.FireListChanging(New ItemListChangeEventArgs([Of] T)(-1, Nothing, New List([Of] T)(collection), Me.owner, ItemListChangeAction.Add))
            MyBase.AddRange(collection)
            Me.FireListChanged(New ItemListChangeEventArgs([Of] T)(MyBase.Count, Nothing, New List([Of] T)(collection), Me.owner, ItemListChangeAction.Add))
        End Sub

        Public Sub Clear()
            DirectCast(Me, ICollection([Of] T)).Clear
        End Sub

        Protected Overridable Sub FireListChanged(ByVal eventArgs As ItemListChangeEventArgs([Of] T))
            If (Not Me.ListChanged Is Nothing) Then
                Me.ListChanged.Invoke(Me, eventArgs)
            End If
        End Sub

        Protected Overridable Sub FireListChanging(ByVal eventArgs As ItemListChangeEventArgs([Of] T))
            If (Not Me.ListChanging Is Nothing) Then
                Me.ListChanging.Invoke(Me, eventArgs)
            End If
        End Sub

        Public Sub Insert(ByVal index As Integer, ByVal item As T)
            DirectCast(Me, IList([Of] T)).Insert(index, item)
        End Sub

        Public Sub InsertRange(ByVal index As Integer, ByVal collection As IEnumerable([Of] T))
            If ((index < 0) OrElse (index > MyBase.Count)) Then
                Throw New ArgumentOutOfRangeException
            End If
            If (collection Is Nothing) Then
                Throw New ArgumentNullException("collection")
            End If
            Me.FireListChanging(New ItemListChangeEventArgs([Of] T)(index, Nothing, New List([Of] T)(collection), Me.owner, ItemListChangeAction.Add))
            MyBase.InsertRange(index, collection)
            Me.FireListChanged(New ItemListChangeEventArgs([Of] T)(index, Nothing, New List([Of] T)(collection), Me.owner, ItemListChangeAction.Add))
        End Sub

        Public Function Remove(ByVal item As T) As Boolean
            Return DirectCast(Me, ICollection([Of] T)).Remove(item)
        End Function

        Public Sub RemoveAt(ByVal index As Integer)
            DirectCast(Me, IList([Of] T)).RemoveAt(index)
        End Sub

        Private Sub System.Collections.Generic.ICollection<T>.Add(ByVal item As T) Implements ICollection([Of] T).Add
            If (item Is Nothing) Then
                Throw New ArgumentNullException("item")
            End If
            Me.FireListChanging(New ItemListChangeEventArgs([Of] T)(MyBase.Count, CType(Nothing, T), item, Me.owner, ItemListChangeAction.Add))
            MyBase.Add(item)
            Me.FireListChanged(New ItemListChangeEventArgs([Of] T)(MyBase.Count, CType(Nothing, T), item, Me.owner, ItemListChangeAction.Add))
        End Sub

        Private Sub System.Collections.Generic.ICollection<T>.Clear() Implements ICollection([Of] T).Clear
            Dim range As ICollection([Of] T) = MyBase.GetRange(0, MyBase.Count)
            Me.FireListChanging(New ItemListChangeEventArgs([Of] T)(-1, range, Nothing, Me.owner, ItemListChangeAction.Remove))
            MyBase.Clear
            Me.FireListChanged(New ItemListChangeEventArgs([Of] T)(-1, range, Nothing, Me.owner, ItemListChangeAction.Remove))
        End Sub

        Private Function System.Collections.Generic.ICollection<T>.Contains(ByVal item As T) As Boolean Implements ICollection([Of] T).Contains
            Return MyBase.Contains(item)
        End Function

        Private Sub System.Collections.Generic.ICollection<T>.CopyTo(ByVal array As T(), ByVal arrayIndex As Integer) Implements ICollection([Of] T).CopyTo
            MyBase.CopyTo(array, arrayIndex)
        End Sub

        Private Function System.Collections.Generic.ICollection<T>.Remove(ByVal item As T) As Boolean Implements ICollection([Of] T).Remove
            If MyBase.Contains(item) Then
                Dim index As Integer = MyBase.IndexOf(item)
                If (index >= 0) Then
                    Me.FireListChanging(New ItemListChangeEventArgs([Of] T)(index, item, CType(Nothing, T), Me.owner, ItemListChangeAction.Remove))
                    MyBase.Remove(item)
                    Me.FireListChanged(New ItemListChangeEventArgs([Of] T)(index, item, CType(Nothing, T), Me.owner, ItemListChangeAction.Remove))
                    Return True
                End If
            End If
            Return False
        End Function

        Private Function System.Collections.Generic.IEnumerable<T>.GetEnumerator() As IEnumerator([Of] T) Implements IEnumerable([Of] T).GetEnumerator
            Return MyBase.GetEnumerator
        End Function

        Private Function System.Collections.Generic.IList<T>.IndexOf(ByVal item As T) As Integer Implements IList([Of] T).IndexOf
            Return MyBase.IndexOf(item)
        End Function

        Private Sub System.Collections.Generic.IList<T>.Insert(ByVal index As Integer, ByVal item As T) Implements IList([Of] T).Insert
            If ((index < 0) OrElse (index > MyBase.Count)) Then
                Throw New ArgumentOutOfRangeException
            End If
            If (item Is Nothing) Then
                Throw New ArgumentNullException("item")
            End If
            Me.FireListChanging(New ItemListChangeEventArgs([Of] T)(index, CType(Nothing, T), item, Me.owner, ItemListChangeAction.Add))
            MyBase.Insert(index, item)
            Me.FireListChanged(New ItemListChangeEventArgs([Of] T)(index, CType(Nothing, T), item, Me.owner, ItemListChangeAction.Add))
        End Sub

        Private Sub System.Collections.Generic.IList<T>.RemoveAt(ByVal index As Integer) Implements IList([Of] T).RemoveAt
            If ((index < 0) OrElse (index > MyBase.Count)) Then
                Throw New ArgumentOutOfRangeException
            End If
            Dim removedActivity As T = MyBase.Item(index)
            Me.FireListChanging(New ItemListChangeEventArgs([Of] T)(index, removedActivity, CType(Nothing, T), Me.owner, ItemListChangeAction.Remove))
            MyBase.RemoveAt(index)
            Me.FireListChanged(New ItemListChangeEventArgs([Of] T)(index, removedActivity, CType(Nothing, T), Me.owner, ItemListChangeAction.Remove))
        End Sub

        Private Sub System.Collections.ICollection.CopyTo(ByVal array As Array, ByVal index As Integer) Implements ICollection.CopyTo
            Dim i As Integer
            For i = 0 To MyBase.Count - 1
                array.SetValue(Me.Item(i), CInt((i + index)))
            Next i
        End Sub

        Private Function System.Collections.IEnumerable.GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
            Return MyBase.GetEnumerator
        End Function

        Private Function System.Collections.IList.Add(ByVal value As Object) As Integer Implements IList.Add
            If Not TypeOf value Is T Then
                Throw New Exception(SR.GetString("Error_InvalidListItem", New Object() { MyBase.GetType.GetGenericArguments(0).FullName }))
            End If
            DirectCast(Me, ICollection([Of] T)).Add(DirectCast(value, T))
            Return (MyBase.Count - 1)
        End Function

        Private Sub System.Collections.IList.Clear() Implements IList.Clear
            DirectCast(Me, ICollection([Of] T)).Clear
        End Sub

        Private Function System.Collections.IList.Contains(ByVal value As Object) As Boolean Implements IList.Contains
            If Not TypeOf value Is T Then
                Throw New Exception(SR.GetString("Error_InvalidListItem", New Object() { MyBase.GetType.GetGenericArguments(0).FullName }))
            End If
            Return DirectCast(Me, ICollection([Of] T)).Contains(DirectCast(value, T))
        End Function

        Private Function System.Collections.IList.IndexOf(ByVal value As Object) As Integer Implements IList.IndexOf
            If Not TypeOf value Is T Then
                Throw New Exception(SR.GetString("Error_InvalidListItem", New Object() { MyBase.GetType.GetGenericArguments(0).FullName }))
            End If
            Return DirectCast(Me, IList([Of] T)).IndexOf(DirectCast(value, T))
        End Function

        Private Sub System.Collections.IList.Insert(ByVal index As Integer, ByVal value As Object) Implements IList.Insert
            If Not TypeOf value Is T Then
                Throw New Exception(SR.GetString("Error_InvalidListItem", New Object() { MyBase.GetType.GetGenericArguments(0).FullName }))
            End If
            DirectCast(Me, IList([Of] T)).Insert(index, DirectCast(value, T))
        End Sub

        Private Sub System.Collections.IList.Remove(ByVal value As Object) Implements IList.Remove
            If Not TypeOf value Is T Then
                Throw New Exception(SR.GetString("Error_InvalidListItem", New Object() { MyBase.GetType.GetGenericArguments(0).FullName }))
            End If
            DirectCast(Me, ICollection([Of] T)).Remove(DirectCast(value, T))
        End Sub


        ' Properties
        Private ReadOnly Property IsFixedSize As Boolean
            Get
                Return False
            End Get
        End Property

        Public Default Property Item(ByVal index As Integer) As T
            Get
                Return Me.Item(index)
            End Get
            Set(ByVal value As T)
                Me.Item(index) = value
            End Set
        End Property

        Protected ReadOnly Property Owner As Object
            Get
                Return Me.owner
            End Get
        End Property

        Private ReadOnly Property System.Collections.Generic.ICollection<T>.Count As Integer
            Get
                Return MyBase.Count
            End Get
        End Property

        Private ReadOnly Property System.Collections.Generic.ICollection<T>.IsReadOnly As Boolean
            Get
                Return False
            End Get
        End Property

        Private Property System.Collections.Generic.IList<T>.Item(ByVal index As Integer) As T
            Get
                Return MyBase.Item(index)
            End Get
            Set(ByVal value As T)
                If (value Is Nothing) Then
                    Throw New ArgumentNullException("item")
                End If
                Dim removedActivity As T = MyBase.Item(index)
                Me.FireListChanging(New ItemListChangeEventArgs([Of] T)(index, removedActivity, value, Me.owner, ItemListChangeAction.Replace))
                MyBase.Item(index) = value
                Me.FireListChanged(New ItemListChangeEventArgs([Of] T)(index, removedActivity, value, Me.owner, ItemListChangeAction.Replace))
            End Set
        End Property

        Private ReadOnly Property System.Collections.ICollection.IsSynchronized As Boolean
            Get
                Return False
            End Get
        End Property

        Private ReadOnly Property System.Collections.ICollection.SyncRoot As Object
            Get
                Return Me
            End Get
        End Property

        Private ReadOnly Property System.Collections.IList.IsFixedSize As Boolean
            Get
                Return False
            End Get
        End Property

        Private ReadOnly Property System.Collections.IList.IsReadOnly As Boolean
            Get
                Return Me.IsReadOnly
            End Get
        End Property

        Private Property System.Collections.IList.Item(ByVal index As Integer) As Object
            Get
                Return Me.Item(index)
            End Get
            Set(ByVal value As Object)
                If Not TypeOf value Is T Then
                    Throw New Exception(SR.GetString("Error_InvalidListItem", New Object() { MyBase.GetType.GetGenericArguments(0).FullName }))
                End If
                Me.Item(index) = DirectCast(value, T)
            End Set
        End Property


        ' Fields
        Private ListChanged As ItemListChangeEventHandler([Of] T)
        Private ListChanging As ItemListChangeEventHandler([Of] T)
        Private owner As Object
    End Class
End Namespace

